/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.commons.runtime;

import io.iec.edp.caf.common.environment.enums.AppEdition;
import io.iec.edp.caf.commons.runtime.env.EnvironmentStateOperator;
import io.iec.edp.caf.commons.runtime.env.entities.EnvironmentState;
import io.iec.edp.caf.commons.runtime.env.enums.GrayReleaseState;
import io.iec.edp.caf.commons.runtime.env.enums.StartupMode;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.springframework.boot.system.ApplicationHome;
import org.springframework.core.env.Environment;
import org.springframework.util.StringUtils;
import java.io.File;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Objects;

/**
 * 应用版本:
 *
 * @author guowenchang
 */
@Slf4j
//@Component
public class CafEnvironment {

    private static Environment environment;

    //版本:单机版/标准版
    private static AppEdition appEdition;

    //端口
    private static Integer port;

    //Base盘名称 支持从配置中获取
    final private static String SERVER_RUNTIME_PATH_NAME = "jstack";

    //Base盘名称 支持从配置中获取
    final private static int SERVER_RUNTIME_PATH_DEPTH = 5;

    //runtime目录
    private static String serverRTPath;
    //基目录
    private static String basePath;
    //后台启动目录
    private static String startupPath;
    //服务器基地址
    private static String serverBaseUrl="";

    /**
     * 设置Java Environment
     * @param environment
     */
    protected static void setEnvironment(Environment environment) {
        CafEnvironment.environment = environment;
    }

    /**
     * 返回Java Environment
     * @return -
     */
    public static Environment getEnvironment() {
        return CafEnvironment.environment;
    }

    /**
     * 返回应用版本类型
     * @return -
     */
    public static AppEdition getAppEdition() {
        if (appEdition == null && environment!=null) {
            appEdition = AppEdition.getMode(environment.getProperty("spring.application.edition"));
        }

        return appEdition;
    }

    /**
     * 返回Port
     * @return -
     */
    public static Integer getPort() {
        if (port == null) {
            port = environment.getProperty("server.port", Integer.class);
        }

        return port;
    }

    public static StartupMode getStartupMode(){
        boolean ifParallel = System.getProperty("parallel.startup", "false").equals("true");
        if(ifParallel){
            return StartupMode.Parallel;
        }else{
            return StartupMode.Serial;
        }
    }

    /**
     * 获取本机ip     *
     * @return -
     */
    public static String getLocalIp(String ipFilter) {
        if (StringUtils.isEmpty(ipFilter)) {
            ipFilter = "";
        }
        //做个处理 移除右侧的*和.
        while (ipFilter.endsWith(".") || ipFilter.endsWith("*")) {
            ipFilter = ipFilter.substring(0, ipFilter.length() - 1);
        }

        Enumeration<NetworkInterface> networks = null;
        try {
            networks = NetworkInterface.getNetworkInterfaces();
            InetAddress ip = null;
            List<NetworkInterface> networkInterfaceList = new ArrayList<>();
            while (networks.hasMoreElements()) {
                networkInterfaceList.add(networks.nextElement());
            }

            for (NetworkInterface networkInterface : networkInterfaceList) {
                Enumeration<InetAddress> addr;
                addr = networkInterface.getInetAddresses();
                while (addr.hasMoreElements()) {
                    ip = addr.nextElement();
                    if (ip != null
                            && ip instanceof Inet4Address
                            && ip.getHostAddress().startsWith(ipFilter)
                    ) {
                        return ip.getHostAddress();
                    }
                }
            }
        } catch (SocketException e) {
            log.error(e.getMessage(),e);
        }

        throw new RuntimeException("can not get local ip address");
    }

    /**
     * 返回运行时目录名称
     * @return -
     */
    public static String getServerRuntimePathName() {
        String srvPath =  System.getProperty("server.runtime.path.name", SERVER_RUNTIME_PATH_NAME);
        if(srvPath==null) return "server";
        //识别为相对路径server或jstack直接返回
        if(srvPath!=null&&(srvPath.equals("server")||srvPath.equals(SERVER_RUNTIME_PATH_NAME))){
            return srvPath;
        }
        //识别为全路径则返回最后的路径
        String pathFromFullPath =  new File(srvPath).getName();
        if(pathFromFullPath!=null&&(pathFromFullPath.equals("server")||pathFromFullPath.equals(SERVER_RUNTIME_PATH_NAME))){
            return pathFromFullPath;
        }
        return srvPath;
    }

    /**
     * 返回层级深度
     * @return -
     */
    private static Integer getServerRuntimePathDepth() {
        String depth = System.getProperty("server.runtime.path.depth", String.valueOf(SERVER_RUNTIME_PATH_DEPTH));
        return Integer.parseInt(depth);
    }

    /**
     * 获取启动类所在的目录(runtime)
     *
     * @return -
     */
    public static String getStartupPath() {
        if (startupPath == null) {
            ApplicationHome home = new ApplicationHome(getMainClass());
            // returns the folder where the jar is. This is what I wanted.
            startupPath = home.getDir().getAbsolutePath().replaceAll("\\\\", "/");
        }

        return startupPath;
    }

    public static String getRuntimePath() {
        if (startupPath == null) {
            ApplicationHome home = new ApplicationHome(getMainClass());
            // returns the folder where the jar is. This is what I wanted.
            String path = home.getDir().getParentFile().getAbsolutePath().replaceAll("\\\\", "/");
            startupPath = path;
        }

        return startupPath;
    }

    /**
     * 获取盘目录
     *
     * @return -
     */
    public static String getBasePath() {
        if (basePath == null) {
            File file = new File(getServerRTPath());
            basePath = file.getParentFile().getAbsolutePath().replaceAll("\\\\", "/");
        }

        return basePath;
    }

    /**
     * 返回灰度表后缀信息
     * @return 灰度表后缀
     */
    public static String getGrayTableSUFFIX(){
        return "_gray";
    }

    /**
     * 获取服务器灰度状态
     * @return
     */
    public static GrayReleaseState getCurrentGrayReleaseState(){
        return EnvironmentStateOperator.getCurrentEnvironmentState(getServerRTPath()).getGrayReleaseState();
    }

    /**
     * 设置服务器灰度状态
     * @param grayReleaseState
     */
    public static void changeCurrentGrayReleaseState(GrayReleaseState grayReleaseState){
        EnvironmentState envState = EnvironmentStateOperator.getCurrentEnvironmentState(getServerRTPath());
        if(!GrayReleaseState.changeEnable(envState.getGrayReleaseState(),grayReleaseState)){
            //throw new RuntimeException("Illegal target state");
        }
        envState.setGrayReleaseState(grayReleaseState);
        EnvironmentStateOperator.updateCurrentEnvironmentState(envState,getServerRTPath());
    }

    /**
     * 获取主应用目录(jstack)
     *
     * @return -
     */
    public static String getServerRTPath() {
        if (serverRTPath == null) {
            String nowDir = getStartupPath();
            File file = new File(nowDir);
            String targetName = getServerRuntimePathName();

            //先找自定义目录 再找jstack
            serverRTPath = doGetServerRTPath(file, targetName);
            if (serverRTPath == null && !SERVER_RUNTIME_PATH_NAME.equals(targetName)) {
                serverRTPath = doGetServerRTPath(file, SERVER_RUNTIME_PATH_NAME);
            }

            if (serverRTPath == null) {
                throw new RuntimeException("Server runtime path not found");
            }
        }

        return serverRTPath;
    }

//    /**
//     * 获取应用服务器网站基地址
//     *
//     * @return -
//     */
//    public static String getServerBaseUrl() {
//        if(!StringUtils.isEmpty(serverBaseUrl))
//            return serverBaseUrl;
//
//        ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        if (servletRequestAttributes != null) {
//            HttpServletRequest request = servletRequestAttributes.getRequest();
//            if (request != null && !StringUtils.isEmpty(request.getScheme()) || !StringUtils.isEmpty(request.getServerName())) {
//                String path = request.getContextPath();
//                if (StringUtils.isEmpty(request.getServerPort()))
//                    serverBaseUrl = request.getScheme() + "://" + request.getServerName() + path;
//                else
//                    serverBaseUrl = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + path;
//            }
//        }
//        return serverBaseUrl;
//    }

    /**
     * 递归查找运行时目录
     * @param file
     * @param targetName
     * @return -
     */
    private static String doGetServerRTPath(File file, String targetName) {
        int depth = getServerRuntimePathDepth();
        while (depth > 0 && file != null) {
            for (File nowFile : Objects.requireNonNull(file.listFiles())) {
                if (nowFile.isDirectory() && nowFile.getName().equals(targetName)) {
                    serverRTPath = nowFile.getPath().replaceAll("\\\\", "/");
                    return serverRTPath;
                }
            }

            file = file.getParentFile();
            depth--;
        }

        return null;
    }

    /**
     * 获取根启动类(PropertiesLaunched)
     *
     * @return -
     */
    private static Class<?> getMainClass() {
        try {
            StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
            for (int i = stackTrace.length - 1; i >= 0; i--) {
                StackTraceElement stackTraceElement = stackTrace[i];
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException ex) {
        }

        return null;
    }

    /**
     * 获取动态基路径
     * @return
     */
    public static String getBaseUrlPath(){
        var contextpath = getEnvironment().getProperty("server.servlet.context-path","/");
        return contextpath.equalsIgnoreCase("/")?"":contextpath;
    }

    /**
     * 获取动态api基路径
     * @return
     */
    public static String getBaseApiPath(){
        var contextpath = getEnvironment().getProperty("server.servlet.context-path","/");
        var cxfpath = getEnvironment().getProperty("cxf.path","/api");
        if(contextpath.equalsIgnoreCase("/")){
            return cxfpath;
        }else{
            return contextpath+cxfpath;
        }
    }

    /**
     * 获取当前进程的du名称
     * @return
     */
    public static String getCurrentDeployunitName(){
        var duname = System.getProperty("deployment-unit.name");
        if(duname==null){
            return "";
        }else{
            return duname;
        }
    }
}
